#include "nmeaparser.h"
#include <QDebug>
#include <QRegularExpression>
#include <cmath>

/**
 * @brief 构造函数
 * @param parent 父对象指针
 */
NmeaParser::NmeaParser(QObject *parent)
    : QObject(parent)
{
}

/**
 * @brief 解析NMEA数据行
 * @param nmeaLine NMEA格式的数据行
 * @return 解析是否成功
 */
bool NmeaParser::parseNmeaLine(const QString &nmeaLine)
{
    if (nmeaLine.isEmpty() || !nmeaLine.startsWith('$')) {
        emit parseError("无效的NMEA数据格式");
        return false;
    }

    // 验证校验和
    if (!validateChecksum(nmeaLine)) {
        emit parseError("NMEA校验和错误");
        return false;
    }

    // 移除校验和部分
    QString cleanLine = nmeaLine.split('*').first();
    QStringList fields = cleanLine.split(',');
    
    if (fields.isEmpty()) {
        emit parseError("NMEA数据字段为空");
        return false;
    }

    QString messageType = fields[0].mid(3); // 移除$GP前缀

    bool success = false;
    if (messageType == "GGA") {
        success = parseGGA(fields);
    } else if (messageType == "RMC") {
        success = parseRMC(fields);
    } else if (messageType == "GSA") {
        success = parseGSA(fields);
    }

    if (success) {
        emit gpsDataUpdated(m_currentGpsData);
    }

    return success;
}

/**
 * @brief 获取当前GPS数据
 * @return GPS数据结构体
 */
GpsData NmeaParser::getCurrentGpsData() const
{
    return m_currentGpsData;
}

/**
 * @brief 验证NMEA校验和
 * @param nmeaLine NMEA数据行
 * @return 校验和是否正确
 */
bool NmeaParser::validateChecksum(const QString &nmeaLine)
{
    int asteriskPos = nmeaLine.indexOf('*');
    if (asteriskPos == -1 || asteriskPos + 3 > nmeaLine.length()) {
        return false;
    }

    QString data = nmeaLine.mid(1, asteriskPos - 1); // 移除$和*
    QString checksumStr = nmeaLine.mid(asteriskPos + 1, 2);

    // 计算校验和
    int checksum = 0;
    for (QChar c : data) {
        checksum ^= c.toLatin1();
    }

    bool ok;
    int expectedChecksum = checksumStr.toInt(&ok, 16);
    
    return ok && (checksum == expectedChecksum);
}

/**
 * @brief 解析GGA语句 (Global Positioning System Fix Data)
 * @param fields 分割后的字段列表
 * @return 解析是否成功
 */
bool NmeaParser::parseGGA(const QStringList &fields)
{
    if (fields.size() < 15) {
        emit parseError("GGA字段数量不足");
        return false;
    }

    try {
        // 时间 (字段1)
        if (!fields[1].isEmpty()) {
            m_currentGpsData.timestamp = parseDateTime(fields[1]);
        }

        // 纬度 (字段2和3)
        if (!fields[2].isEmpty() && !fields[3].isEmpty()) {
            double lat = convertDMMToDecimal(fields[2]);
            if (fields[3] == "S") lat = -lat;
            m_currentGpsData.latitude = lat;
        }

        // 经度 (字段4和5)
        if (!fields[4].isEmpty() && !fields[5].isEmpty()) {
            double lon = convertDMMToDecimal(fields[4]);
            if (fields[5] == "W") lon = -lon;
            m_currentGpsData.longitude = lon;
        }

        // 定位质量 (字段6)
        if (!fields[6].isEmpty()) {
            int quality = fields[6].toInt();
            switch (quality) {
                case 0: m_currentGpsData.quality = "无效"; break;
                case 1: m_currentGpsData.quality = "GPS"; break;
                case 2: m_currentGpsData.quality = "DGPS"; break;
                case 3: m_currentGpsData.quality = "PPS"; break;
                case 4: m_currentGpsData.quality = "RTK"; break;
                case 5: m_currentGpsData.quality = "Float RTK"; break;
                case 6: m_currentGpsData.quality = "估算"; break;
                case 7: m_currentGpsData.quality = "手动"; break;
                case 8: m_currentGpsData.quality = "模拟"; break;
                default: m_currentGpsData.quality = "未知"; break;
            }
            m_currentGpsData.isValid = (quality > 0);
        }

        // 卫星数量 (字段7)
        if (!fields[7].isEmpty()) {
            m_currentGpsData.satellites = fields[7].toInt();
        }

        // 海拔高度 (字段9)
        if (!fields[9].isEmpty()) {
            m_currentGpsData.altitude = fields[9].toDouble();
        }

        return true;
    } catch (...) {
        emit parseError("GGA解析异常");
        return false;
    }
}

/**
 * @brief 解析RMC语句 (Recommended Minimum Course)
 * @param fields 分割后的字段列表
 * @return 解析是否成功
 */
bool NmeaParser::parseRMC(const QStringList &fields)
{
    if (fields.size() < 13) {
        emit parseError("RMC字段数量不足");
        return false;
    }

    try {
        // 状态 (字段2)
        bool isActive = (fields[2] == "A");
        m_currentGpsData.isValid = isActive;

        if (!isActive) {
            return true; // 数据无效但解析成功
        }

        // 时间和日期 (字段1和9)
        if (!fields[1].isEmpty() && !fields[9].isEmpty()) {
            m_currentGpsData.timestamp = parseDateTime(fields[1], fields[9]);
        }

        // 纬度 (字段3和4)
        if (!fields[3].isEmpty() && !fields[4].isEmpty()) {
            double lat = convertDMMToDecimal(fields[3]);
            if (fields[4] == "S") lat = -lat;
            m_currentGpsData.latitude = lat;
        }

        // 经度 (字段5和6)
        if (!fields[5].isEmpty() && !fields[6].isEmpty()) {
            double lon = convertDMMToDecimal(fields[5]);
            if (fields[6] == "W") lon = -lon;
            m_currentGpsData.longitude = lon;
        }

        // 速度 (字段7) - 节
        if (!fields[7].isEmpty()) {
            m_currentGpsData.speed = fields[7].toDouble();
        }

        // 航向 (字段8)
        if (!fields[8].isEmpty()) {
            m_currentGpsData.course = fields[8].toDouble();
        }

        return true;
    } catch (...) {
        emit parseError("RMC解析异常");
        return false;
    }
}

/**
 * @brief 解析GSA语句 (GPS DOP and active satellites)
 * @param fields 分割后的字段列表
 * @return 解析是否成功
 */
bool NmeaParser::parseGSA(const QStringList &fields)
{
    if (fields.size() < 18) {
        emit parseError("GSA字段数量不足");
        return false;
    }

    try {
        // 定位模式 (字段2)
        if (!fields[2].isEmpty()) {
            int fixType = fields[2].toInt();
            switch (fixType) {
                case 1: 
                    m_currentGpsData.quality = "无定位";
                    m_currentGpsData.isValid = false;
                    break;
                case 2: 
                    m_currentGpsData.quality = "2D定位";
                    m_currentGpsData.isValid = true;
                    break;
                case 3: 
                    m_currentGpsData.quality = "3D定位";
                    m_currentGpsData.isValid = true;
                    break;
                default:
                    m_currentGpsData.quality = "未知";
                    break;
            }
        }

        return true;
    } catch (...) {
        emit parseError("GSA解析异常");
        return false;
    }
}

/**
 * @brief 将度分格式转换为十进制度数
 * @param dmm 度分格式字符串 (DDMM.MMMM)
 * @return 十进制度数
 */
double NmeaParser::convertDMMToDecimal(const QString &dmm)
{
    if (dmm.isEmpty()) return 0.0;

    double value = dmm.toDouble();
    int degrees = static_cast<int>(value / 100);
    double minutes = value - (degrees * 100);
    
    return degrees + (minutes / 60.0);
}

/**
 * @brief 解析时间字符串
 * @param timeStr 时间字符串 (HHMMSS.SSS)
 * @param dateStr 日期字符串 (DDMMYY)
 * @return QDateTime对象
 */
QDateTime NmeaParser::parseDateTime(const QString &timeStr, const QString &dateStr)
{
    QDateTime dateTime = QDateTime::currentDateTimeUtc();

    if (!timeStr.isEmpty() && timeStr.length() >= 6) {
        int hour = timeStr.mid(0, 2).toInt();
        int minute = timeStr.mid(2, 2).toInt();
        int second = timeStr.mid(4, 2).toInt();
        int msec = 0;
        
        if (timeStr.length() > 7 && timeStr.contains('.')) {
            QString msecStr = timeStr.mid(7);
            msec = msecStr.leftJustified(3, '0').left(3).toInt();
        }

        QTime time(hour, minute, second, msec);
        dateTime.setTime(time);
    }

    if (!dateStr.isEmpty() && dateStr.length() >= 6) {
        int day = dateStr.mid(0, 2).toInt();
        int month = dateStr.mid(2, 2).toInt();
        int year = dateStr.mid(4, 2).toInt();
        
        // 处理两位年份
        if (year < 80) {
            year += 2000;
        } else {
            year += 1900;
        }

        QDate date(year, month, day);
        dateTime.setDate(date);
    }

    return dateTime;
}